package iptux;

import java.io.File;
import java.io.IOException;  
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;  
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import javax.swing.JOptionPane;
import iptux.StaticConsts;
import iptux.Iptux;
import baseclass.FileInfo;
import baseclass.PalInfo;

/**
 * Title: class IptuxUDPServer<br>
 * Description: IptuxUDPServer class<br>
 * Copyright: MIT open source protocol<br>
 * @author cwll<cwll2009@126.com>
 * @version 1.0.0
 */
public class IptuxUDPServer extends Thread {
 
    private DatagramSocket serverSocket = null;  
    private InetSocketAddress socketAddress = null;
    private Iptux mainDlg = null;
    public String localhost;
    //private String sendStr = null;
    //public static int maindlg.PacketNum = 1;
    
    /**
     * Construction methond.
     * @param localhost IP address of the UDP server
     * @param maindlg	main dialog handler
     * @throws Exception
     */
    public IptuxUDPServer(String localhost,Iptux mainDlg) throws Exception { 
        socketAddress = new InetSocketAddress(localhost, StaticConsts.IPMSG_PORT);  
        serverSocket = new DatagramSocket(socketAddress); 
        //byte[] serverBuffer = new byte[1024];
        //DatagramPacket serverPacket = new DatagramPacket(serverBuffer, serverBuffer.length);
        //System.out.println("UDP server startup! " + localhost);  
        this.mainDlg = mainDlg;
        this.localhost = localhost;
    } 
    
    /**
     * Get the IP address of the UDP server.
     * @return localhost is a string of IP address
     */
    public String getServerIP(){
    	return localhost;
    }
    
    /**
     * Create a PalInfo structure to store the information of peer.
     * @param remoteAddress peer IP address
     * @param data	the IPMSG header and message content
     * @return a PalInfo handler.
     */
    private PalInfo AppendPal(String remoteAddress,String data){
    	PalInfo pal = null;
        if(mainDlg.findPal(remoteAddress) == null){
        	//System.out.println("appending user:"+remoteAddress);
        	pal = new PalInfo();
        	pal.ipv4 = remoteAddress;
        	pal.name = data.split(":")[2];
        	pal.version = data.split(":")[0];
			if (pal.version.indexOf("iptux") > 0)
				pal.iconfile = "icon-tux.png";
			else if (pal.version.indexOf("lbt") > 0)
				pal.iconfile = "icon-feiq.png";
			else if (pal.version.equals("1"))
				pal.iconfile = "icon-feig.png";
			else
				pal.iconfile = "icon-qq.png";
			pal.iconfile = "/pixmaps/icon/" + pal.iconfile;
			pal.host = data.split(":")[3];
			pal.packetn = 0;
			pal.rpacketn = 0;
			mainDlg.appendPal(pal);
        }
         return pal;
    }
    
    /**
     * IptuxUDPServer is the multi thread class, extended from Thread.
     * This method implement the run method of it's supper class, it continually receive UDP data
     * on server port. When receives data, it start a new thread of UDPListenner to process it.
     * 
     */
    public void run(){ 
    	while (true){
    		//System.out.println("UDP server waiting1" + this.localhost);
    		try {
    			//System.out.println("UDP server waiting"+ this.localhost);
    			byte[] serverBuffer = new byte[1024];
    	        DatagramPacket serverPacket = new DatagramPacket(serverBuffer, serverBuffer.length);
    			serverSocket.receive(serverPacket);
    			if (serverPacket.getLength() > 0){
    				byte[] data = serverPacket.getData();
    		        //byte[] data = new byte[serverPacket.getLength()];
    		        //data = Array.CopyTo(serverPacket.getData(),serverPacket.getLength(),data,0,serverPacket.getLength());
    		        String remoteAddress = serverPacket.getAddress().toString();
    		        remoteAddress = remoteAddress.substring(1,remoteAddress.length());
    		        boolean selfSend = false;
    		        for(String ip:Utils.GetLocalIPAddress()){
    		        	if(remoteAddress.equals(ip) ){
    		        		selfSend = true;
    		        	}
    		        }
    		        if(selfSend == true){
    		        	continue;
    		        }
    		        //System.out.println("接收信息from：" + remoteAddress);  
    		        //this.UDPDespatcher(remoteAddress);
    		        UDPListenner listenner = new UDPListenner(this,data,remoteAddress);
    		        new Thread(listenner).start();
    	        }
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				System.out.println("error receive udp");
			}
    	}
    }
    
    /**
     * Broadcast entry message to local net.
     */
    public void BroadcastEntry(){
    	//广播地址
    	String host = localhost.substring(0, localhost.lastIndexOf("."));
    	host += ".255";
    	//System.out.println(host);
    	int Command = StaticConsts.IPMSG_ABSENCEOPT | StaticConsts.IPMSG_BR_ENTRY;
        String sendStr = Utils.MakeCommand(Command);
        sendStr += "iptux-ubuntu";
        //System.out.println(sendStr);
        try {
            InetAddress adds = InetAddress.getByName(host);
            DatagramPacket dp = new DatagramPacket(sendStr.getBytes(),
            		sendStr.length(), adds, StaticConsts.IPMSG_PORT);
            serverSocket.send(dp);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        //dial up the addresses in configer.broadcastRanges
        if(mainDlg.configer.broadcastRanges != null){
			if (mainDlg.configer.broadcastRanges.length > 0) {
				for (String ipPairs : mainDlg.configer.broadcastRanges) {
					String ipFrom = ipPairs.split("-->")[0].trim();
					if (!Utils.sameSubnet(ipFrom, this.getServerIP())) {
						continue;
					}
					String ipTo = ipPairs.split("-->")[1].trim();
					String subnet = ipFrom.substring(0, ipFrom.lastIndexOf("."));
					int startIp = Integer.valueOf(ipFrom.split(".")[3]);
					int endIp = Integer.valueOf(ipTo.split(".")[3]);
					for (int ip = startIp; ip <= endIp; ip++) {
						String destIp = subnet + String.valueOf(ip);
						Dialup(destIp);
					}
				}
			}
        }
    }
    
    /**
     * Broadcast exit message to local net.
     */
    public void BroadcastExit(){
    	//广播地址
    	String host = localhost.substring(0, localhost.lastIndexOf("."));
    	host += ".255";
    	//System.out.println("UDP server " + localhost + " exit.");
    	int Command = StaticConsts.IPMSG_BR_EXIT;
    	//maindlg.PacketNum++;
        String sendStr = Utils.MakeCommand(Command);
        sendStr += "iptux-ubuntu";
        try {
            InetAddress adds = InetAddress.getByName(host);
            DatagramPacket dp = new DatagramPacket(sendStr.getBytes(),
            		sendStr.length(), adds, StaticConsts.IPMSG_PORT);
            serverSocket.send(dp);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }	
    }
    
    /**
     * Dialup entry message to a peer.
     * @param ipaddr peer IP address
     */
    public static void Dialup(String ipaddr){
    	//PalInfo pal = this.maindlg.findPal(ipaddr);
    	//pal.rpacketn++;
    	//maindlg.PacketNum++;
    	byte[] buf = new byte[1024]; 
        int Command = StaticConsts.IPMSG_ABSENCEOPT | StaticConsts.IPMSG_BR_ENTRY;
        String sendStr = Utils.MakeCommand(Command);
        sendStr += "iptux-ubuntu";
        try {
        	buf = sendStr.getBytes();
        	DatagramSocket dataSocket = new DatagramSocket();
        	DatagramPacket dataPacket = new DatagramPacket(buf, buf.length, InetAddress.getByName(ipaddr),
					StaticConsts.IPMSG_PORT);
        	dataSocket.send(dataPacket);
        	dataSocket.close();
        } catch (IOException e) {
    	   // TODO Auto-generated catch block
    	   e.printStackTrace();
        }
    }
    
    /**
     * Send a message.
     * @param host	peer IP address
     * @param msg	string message to send
     * @param Command	IPMSG command
     * @param Packetn	IPMSG packet number
     * @return
     */
    public boolean SendMSG(String host,String msg,int Command,String Packetn){
    	//PalInfo pal = this.maindlg.findPal(host);
    	String sendStr = Utils.MakeCommand(Command,Packetn);
        sendStr += msg;
        //System.out.print(msg);
        //System.out.print(sendStr);
        try {
        	byte[] clientBuffer = sendStr.getBytes(this.mainDlg.configer.msgEncoding);
			DatagramPacket clientPacket = new DatagramPacket(clientBuffer, clientBuffer.length, InetAddress.getByName(host),
					StaticConsts.IPMSG_PORT);
			serverSocket.send(clientPacket);
        } catch (IOException e) {
    	   // TODO Auto-generated catch block
    	   e.printStackTrace();
        }
    	return true;
    }
    
    /**
     * Send a message.
     * @param host	peer IP address
     * @param msg	string message to send
     * @param Command	IPMSG command
     * @return
     */
    public boolean SendMSG(String host,String msg,int Command){
    	PalInfo pal = this.mainDlg.findPal(host);
    	//maindlg.PacketNum++;
    	String sendStr = Utils.MakeCommand(Command);
    	String packn = Utils.getPacketNumFromCommand(sendStr);
    	if(pal instanceof  PalInfo)
    		pal.packetn = Long.valueOf(packn);
        sendStr += msg;
        //System.out.print(sendStr);
        try {
        	byte[] clientBuffer = sendStr.getBytes(this.mainDlg.configer.msgEncoding);
			DatagramPacket clientPacket = new DatagramPacket(clientBuffer, clientBuffer.length, InetAddress.getByName(host),
					StaticConsts.IPMSG_PORT);
			serverSocket.send(clientPacket);
        } catch (IOException e) {
    	   // TODO Auto-generated catch block
    	   e.printStackTrace();
        }
    	return true;
    }
    
    public boolean SendFileInfo(ArrayList<FileInfo> SendFileList,String IPAddress){
    	int Command = StaticConsts.IPMSG_SENDMSG | StaticConsts.IPMSG_FILEATTACHOPT;
    	//String sendStr = Utils.MakeCommand(Command);
    	//String rpackn = Utils.getPacketNumFromCommand(sendStr);
    	String rpackn = Utils.getPacketNum();
    	int index = 0;
    	String sendStr = "\0";
    	//maindlg.PacketNum++;
    	for(FileInfo fileInfo:SendFileList){
    		fileInfo.packetn = Integer.valueOf(rpackn);
/*    		'\0'
    		fileID:filename:size:mtime:fileattr[:extend-attr=val1
    		[,val2...][:extend-attr2=...]]:\a:fileID.*/
    		fileInfo.fileid = index;
    		sendStr = sendStr + "" + Integer.toHexString(index);
    		File file = new File(fileInfo.filepath);
    		sendStr = sendStr + ":" + file.getName();
    		sendStr = sendStr + ":" + Long.toHexString(Long.valueOf(fileInfo.filesize));
    		sendStr = sendStr + ":0:";
    		sendStr = sendStr + Long.toHexString(Long.valueOf(fileInfo.fileattr))+":\7:";
    		this.mainDlg.sendFileList.add(fileInfo);
    		index++;
    	}
    	this.SendMSG(IPAddress, sendStr, Command, Long.toHexString(Long.valueOf(rpackn)));
    	//System.out.println("Send file info packetn is:" + Long.toHexString(Long.valueOf(rpackn)));
    	//this.SendMSG(IPAddress, sendStr, Command, rpackn);
    	return true;
    }
    
    /**
     * UDP data process class multi thread.
     * @author cwll<cwll2009@126.com>
     *
     */
    class UDPListenner implements Runnable{
    	//byte[] buffer = null;
    	String data = null;
    	String RemoteHost = null;
    	IptuxUDPServer UDPServer = null;
    	int commandno = 0;
    	int commandmod = 0;
    	int commandopt = 0;
    	
    	/**
    	 * 
    	 * @param UDPServer
    	 * @param data
    	 * @param host
    	 */
    	public UDPListenner(IptuxUDPServer UDPServer,byte[] buffer,String host){
        	//this.data = new String(buffer,0,buffer.length);
    		try {
				this.data = new String(buffer,"GB18030");
			} catch (UnsupportedEncodingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        	this.RemoteHost = host;
        	this.UDPServer = UDPServer;
        	this.commandno = Integer.parseInt(data.split(":")[4]);
        	this.commandmod = StaticConsts.GET_MODE(commandno);
        	this.commandopt = StaticConsts.GET_OPT(commandno);
    	}
    	
    	/**
    	 * run method
    	 */
    	public void run(){
    		this.UDPDespatcher();
    	}
    	
    	/**
    	 * UDP data despatcher
    	 */
        public void UDPDespatcher(){
        	//System.out.print("Despatching data from "+this.RemoteHost+" data:"+data);
/*        	if (data.split(":")[4] == "121")
        		return;*/
        	switch (this.commandmod){
        		case StaticConsts.IPMSG_BR_ENTRY:
        			this.SomeoneEntry();
        			break;
        		case StaticConsts.IPMSG_BR_EXIT:
        			this.SomeoneExit();
        			break;
/*        		case StaticConsts.FEIQ_BR_EXIT:
        			this.SomeoneExit();
        			break;*/
        		case StaticConsts.IPMSG_ANSENTRY:
        			this.SomeoneAnsentry();
        			break;
        		case StaticConsts.IPMSG_BR_ABSENCE:
        			this.SomeoneAbsence();
        			break;
        		case StaticConsts.IPMSG_SENDMSG:
        			this.SomeoneSendMSG();
        			break;
        		case StaticConsts.IPMSG_RECVMSG:
        			this.SomeoneRecvmsg();
        			break;
        		case StaticConsts.IPTUX_ASKSHARED:
        			this.SomeoneAskShared();
        			break;
        		case StaticConsts.IPTUX_SENDICON:
        			this.SomeoneSendIcon();
        			break;
        		case StaticConsts.IPTUX_SENDSIGN:
        			this.SomeoneSendSign();
        			break;
        		case StaticConsts.IPTUX_SENDMSG:
        			this.SomeoneBcstmsg();
        			break;
        		default:
        			break;
        	}

        }
        
        /**
         * Process the peer entry data, the message is broadcasted.
         */
        private void SomeoneEntry(){
        	//System.out.println("someone entry:"+this.RemoteHost);
        	//update UI
        	int PacketID = Integer.parseInt(data.split(":")[1]);
        	PalInfo pal = this.UDPServer.mainDlg.findPal(this.RemoteHost);
        	
        	if (pal == null)
        		pal = AppendPal(this.RemoteHost,this.data);
        	pal.packetn = PacketID;
        	
        	//send a answer entry feedback to  the pal
        	SendMSG(this.RemoteHost,mainDlg.configer.userName,StaticConsts.IPMSG_ANSENTRY);
        }
  
        /**
         * Process the peer exit data
         */
        private void SomeoneExit(){
        	//System.out.println("someone exit:"+this.RemoteHost);
        	mainDlg.deletePal(this.RemoteHost);
        	mainDlg.updatePalListUI();
        }
        
        /**
         * Process the entry reply data
         */
        private void SomeoneAnsentry(){
        	//System.out.println("someone Ansentry:"+this.RemoteHost);
        	//update UI
        	//int PacketID = Integer.parseInt(data.split(":")[1]);
        	PalInfo pal = this.UDPServer.mainDlg.findPal(this.RemoteHost);
        	if (pal == null){
        		pal = AppendPal(this.RemoteHost,this.data);
        	}
        	String palName = data.split(":")[5];
        	palName = palName.trim();
        	pal.name = palName;
        	mainDlg.updatePalListUI();
        	SendMSG(this.RemoteHost,"",StaticConsts.IPMSG_RECVMSG);
        }
        
        /**
         * Process the entry reply data, the message is dialed up.
         */       
        private void SomeoneAbsence(){
        	this.SomeoneEntry();
        	Dialup(this.RemoteHost);
        }
        
        /**
         * Process the message receive data.
         */       
        private void SomeoneSendMSG(){
        	//test if the message have received
        	int PacketID = Integer.parseInt(data.split(":")[1]);
        	PalInfo pal = this.UDPServer.mainDlg.findPal(this.RemoteHost);
/*        	if (PacketID <= pal.packetn)
        		return;*/
        	pal.packetn = PacketID;
        	//test is need a feedback.
    		if ((this.commandopt & StaticConsts.IPMSG_SENDCHECKOPT) > 0)
    			this.ReceiveMSGConfirm(this.RemoteHost,PacketID);
    		
        	//different message type have different process branch
    		//int test = this.commandopt & StaticConsts.IPMSG_FILEATTACHOPT;
            if ((this.commandopt & StaticConsts.IPMSG_FILEATTACHOPT) > 0){
            	//System.out.println("File receive");
                this.SomeOneSendFile(this.data);
            }else{
            	this.SomeoneSendTextMSG();
            }
        	SendMSG(this.RemoteHost,"",StaticConsts.IPMSG_RECVMSG);
        }
        
        /**
         * Process the file message receive data.
         */         
        public void SomeOneSendFile(String data){
        	PalInfo pal = this.UDPServer.mainDlg.findPal(this.RemoteHost);
            if(pal == null){
            	this.SomeoneEntry();
            	pal = this.UDPServer.mainDlg.findPal(this.RemoteHost);
            }
        	DialogPeer DlgPeel = this.UDPServer.mainDlg.findDialogPeer(this.RemoteHost);
        	if (DlgPeel == null){
        		DlgPeel = this.UDPServer.mainDlg.createDialogPeer(this.RemoteHost);
        	} 
        	if (DlgPeel == null) {
        		JOptionPane.showMessageDialog(null, "Error while create chatting dialog!", "Error occuring", JOptionPane.ERROR_MESSAGE);;
        		return;
        	}
            //飞秋发给别人的都是十进制，接收时要十六进制
            int packetID = Integer.valueOf(data.split(":")[1]);
            ReceiveMSGConfirm(this.RemoteHost,packetID);
            String fileAttach = data.split("\0")[1];      
            //System.out.println(fileAttach);
            String[] fileList = fileAttach.split("\7");
            //fileList[fileList.length-1] = null;
            for(String fileStr:fileList){
            	//System.out.println(fileStr);
            	FileInfo fileInfo = new FileInfo();
            	fileInfo.fileid = Integer.valueOf(fileStr.split(":")[0]);
            	fileInfo.filepath = fileStr.split(":")[1];
            	//System.out.println("file size is: " + fileStr.split(":")[2]);
            	//fileInfo.filesize = Integer.toHexString(Integer.valueOf(fileStr.split(":")[2],16));
            	fileInfo.filesize = Long.valueOf(fileStr.split(":")[2],16);
            	fileInfo.fileattr = fileStr.split(":")[4];
            	fileInfo.packetn = packetID;
            	DlgPeel.appendRcvFile(fileInfo);
            }
            //添加到对应的聊天窗口中等待点接收
            DlgPeel.receiveFile();
            //msg=U'%s send you some file, click to receive.'%globalvar.peeldlglist[PalDlgIndex].palinfo.user
            //globalvar.peeldlglist[PalDlgIndex].AppendMsgHistory('receive',utils.EncodeUtf8(msg))
        	SendMSG(this.RemoteHost,"",StaticConsts.IPMSG_RECVMSG);
        }
        
        /**
         * Process the text message receive data.
         */       
        private void SomeoneSendTextMSG(){
        	DialogPeer DlgPeel = this.UDPServer.mainDlg.findDialogPeer(this.RemoteHost);
        	if (DlgPeel == null){
        		DlgPeel = this.UDPServer.mainDlg.createDialogPeer(this.RemoteHost);
        	} /*else {
        		JOptionPane.showMessageDialog(null, "Error while create chatting dialog!", "Error occuring", JOptionPane.ERROR_MESSAGE);;
        		return;
        	}*/
        	DlgPeel.ReceiveMSG(this.data);
        }
        
        /**
         * Process the message receive reply data.
         */         
        private void SomeoneRecvmsg(){
        	PalInfo pal = this.UDPServer.mainDlg.findPal(this.RemoteHost);
        	/*
        	 * use the following line will raise a exception
        	 * pal.rpacketn = Long.valueOf(this.data.split(":")[5].trim());
        	 */
        	if(this.data.split(":")[5].trim().equals("")){
        		return;
        	}
        	long rpacketn =  Long.valueOf(this.data.split(":")[5].trim());
        	pal.rpacketn =  rpacketn;
        }
        private void SomeoneAskShared(){
        	
        }
        private void SomeoneSendIcon(){
        	
        }
        private void SomeoneSendSign(){
        	
        }
        private void SomeoneBcstmsg(){
        	
        }
    	/**
    	 * Send a feedback for the received message.
    	 * @param PacketID
    	 */
    	public void ReceiveMSGConfirm(String IPAddress,int PacketID){
            int Command =  StaticConsts.IPMSG_RECVMSG;
    		UDPServer.SendMSG(IPAddress,Integer.toHexString(PacketID),Command);
    	}
    }
}
